package jxd.myDwr;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.xml.parsers.ParserConfigurationException;

import org.directwebremoting.Container;
import org.directwebremoting.ServerContextFactory;
import org.directwebremoting.WebContextFactory;
import org.directwebremoting.dwrp.DefaultConverterManager;
import org.directwebremoting.dwrp.HtmlCallMarshaller;

import jxd.myDwr.DefaultContainer;
import jxd.myDwr.InterfaceHandler;
import jxd.myDwr.PlainCallHandler;
import jxd.myDwr.PlainCallMarshaller;

import org.directwebremoting.extend.AccessControl;
import org.directwebremoting.extend.AjaxFilterManager;
import org.directwebremoting.extend.Configurator;
import org.directwebremoting.extend.ConverterManager;
import org.directwebremoting.extend.Creator;
import org.directwebremoting.extend.CreatorManager;
import org.directwebremoting.extend.DebugPageGenerator;
import org.directwebremoting.extend.PageNormalizer;
import org.directwebremoting.extend.Remoter;
import org.directwebremoting.extend.ScriptSessionManager;
import org.directwebremoting.extend.ServerLoadMonitor;
import org.directwebremoting.impl.DefaultAccessControl;
import org.directwebremoting.impl.DefaultAjaxFilterManager;
import org.directwebremoting.impl.DefaultCreatorManager;
import org.directwebremoting.impl.DefaultDebugPageGenerator;
import org.directwebremoting.impl.DefaultPageNormalizer;
import org.directwebremoting.impl.DefaultRemoter;
import org.directwebremoting.impl.DefaultScriptSessionManager;
import org.directwebremoting.impl.DefaultServerContextBuilder;
import org.directwebremoting.impl.DefaultServerLoadMonitor;
import org.directwebremoting.impl.DefaultWebContextBuilder;
import org.directwebremoting.impl.DwrXmlConfigurator;
import org.directwebremoting.impl.ThreadDroppingServerLoadMonitor;
import org.directwebremoting.servlet.AboutHandler;
import org.directwebremoting.servlet.AuthHandler;
import org.directwebremoting.servlet.EngineHandler;
import org.directwebremoting.servlet.HtmlCallHandler;
import org.directwebremoting.servlet.HtmlPollHandler;
import org.directwebremoting.servlet.IndexHandler;
import org.directwebremoting.servlet.PlainPollHandler;
import org.directwebremoting.servlet.TestHandler;
import org.directwebremoting.servlet.UtilHandler;
import org.directwebremoting.servlet.WebworkUtilHandler;
import org.directwebremoting.util.LocalUtil;
import org.directwebremoting.util.Logger;
import org.xml.sax.SAXException;

import cn.util.MenuUtil;

public class ContainerUtil
{
  public static final String INIT_CONFIG = "config";
  public static final String INIT_SKIP_DEFAULT = "skipDefaultConfig";
  public static final String INIT_LOGLEVEL = "logLevel";
  public static final String INIT_PUBLISH_CONTAINER = "publishContainerAs";
  public static final String INIT_CUSTOM_CONFIGURATOR = "customConfigurator";
  public static final String ATTRIBUTE_CONTAINER_LIST = "org.directwebremoting.ContainerList";
  private static final Logger log = Logger.getLogger(ContainerUtil.class);

  public static DefaultContainer createDefaultContainer(ServletConfig servletConfig)
    throws ServletException
  {
    try
    {
      String typeName = servletConfig.getInitParameter(Container.class.getName());
      if (typeName == null)
      {
        return new DefaultContainer();
      }

      log.debug("Using alternate Container implementation: " + typeName);
      Class type = LocalUtil.classForName(typeName);
      return (DefaultContainer)type.newInstance();
    }
    catch (Exception ex) {
    }
    throw new ServletException();
  }

  public static void setupDefaultContainer(DefaultContainer container, ServletConfig servletConfig)
    throws InstantiationException, IllegalAccessException
  {
    setupDefaults(container, servletConfig);
    setupFromServletConfig(container, servletConfig);
    container.setupFinished();
  }

  public static void setupDefaults(DefaultContainer container, ServletConfig servletConfig)
    throws InstantiationException, IllegalAccessException
  { 
    container.addParameter(AccessControl.class.getName(), DefaultAccessControl.class.getName());
    container.addParameter(ConverterManager.class.getName(), DefaultConverterManager.class.getName());
    container.addParameter(CreatorManager.class.getName(), DefaultCreatorManager.class.getName());
    container.addParameter(UrlProcessor.class.getName(), UrlProcessor.class.getName());
    container.addParameter(WebContextFactory.WebContextBuilder.class.getName(), DefaultWebContextBuilder.class.getName());
    container.addParameter(ServerContextFactory.ServerContextBuilder.class.getName(), DefaultServerContextBuilder.class.getName());
    container.addParameter(AjaxFilterManager.class.getName(), DefaultAjaxFilterManager.class.getName());
    container.addParameter(Remoter.class.getName(), DefaultRemoter.class.getName());
    container.addParameter(DebugPageGenerator.class.getName(), DefaultDebugPageGenerator.class.getName());
    container.addParameter(PlainCallMarshaller.class.getName(), PlainCallMarshaller.class.getName());
    container.addParameter(HtmlCallMarshaller.class.getName(), HtmlCallMarshaller.class.getName());
    container.addParameter(PlainPollHandler.class.getName(), PlainPollHandler.class.getName());
    container.addParameter(HtmlPollHandler.class.getName(), HtmlPollHandler.class.getName());
    container.addParameter(ScriptSessionManager.class.getName(), DefaultScriptSessionManager.class.getName());
    container.addParameter(PageNormalizer.class.getName(), DefaultPageNormalizer.class.getName());

    if (servletConfig.getServletContext().getServerInfo().startsWith("jetty-6"))
    {
      container.addParameter(ServerLoadMonitor.class.getName(), ThreadDroppingServerLoadMonitor.class.getName());
    }
    else
    {
      container.addParameter(ServerLoadMonitor.class.getName(), DefaultServerLoadMonitor.class.getName());
    }

    createUrlMapping(container, "/index.html", "indexHandlerUrl", IndexHandler.class);
    createUrlMapping(container, "/engine.js", "engineHandlerUrl", EngineHandler.class);
    createUrlMapping(container, "/util.js", "utilHandlerUrl", UtilHandler.class);
    createUrlMapping(container, "/auth.js", "authHandlerUrl", AuthHandler.class);
    createUrlMapping(container, "/webwork/DWRActionUtil.js", "webworkUtilHandlerUrl", WebworkUtilHandler.class);
    createUrlMapping(container, "/about", "aboutHandlerUrl", AboutHandler.class);
    createUrlMapping(container, "/test/", "testHandlerUrl", TestHandler.class);
    createUrlMapping(container, "/interface/", "interfaceHandlerUrl", InterfaceHandler.class);

    createUrlMapping(container, "/call/plaincall/", "plainCallHandlerUrl", PlainCallHandler.class);
    createUrlMapping(container, "/call/plainpoll/", "plainPollHandlerUrl", PlainPollHandler.class);
    createUrlMapping(container, "/call/htmlcall/", "htmlCallHandlerUrl", HtmlCallHandler.class);
    createUrlMapping(container, "/call/htmlpoll/", "htmlPollHandlerUrl", HtmlPollHandler.class);
  }

  public static void createUrlMapping(DefaultContainer container, String url, String propertyName, Class handler)
    throws InstantiationException, IllegalAccessException
  {
    container.addParameter("url:" + url, handler.getName());
    container.addParameter(propertyName, url);
  }

  public static void setupFromServletConfig(DefaultContainer container, ServletConfig servletConfig)
    throws InstantiationException, IllegalAccessException
  {
    Enumeration en = servletConfig.getInitParameterNames();
    while (en.hasMoreElements())
    {
      String name = (String)en.nextElement();
      String value = servletConfig.getInitParameter(name);
      container.addParameter(name, value);
    }
  }

  public static void prepareForWebContextFilter(ServletContext context, ServletConfig config, Container container, WebContextFactory.WebContextBuilder webContextBuilder, HttpServlet servlet)
  {
    context.setAttribute(Container.class.getName(), container);
    context.setAttribute(WebContextFactory.WebContextBuilder.class.getName(), webContextBuilder);
    context.setAttribute(ServletConfig.class.getName(), config);
    context.setAttribute(HttpServlet.class.getName(), servlet);
  }

  public static void configureFromSystemDwrXml(Container container)
    throws IOException, ParserConfigurationException, SAXException
  {
    DwrXmlConfigurator system = new DwrXmlConfigurator();
    system.setClassResourceName("/org/directwebremoting/dwr.xml");
    system.configure(container);
  }

  public static void configureFromDefaultDwrXml(Container container)
    throws IOException, ParserConfigurationException, SAXException
  {
    DwrXmlConfigurator local = new DwrXmlConfigurator();
    local.setServletResourceName("/WEB-INF/dwr.xml");
    local.configure(container);
  }

  public static boolean configureFromInitParams(Container container, ServletConfig servletConfig)
    throws IOException, ParserConfigurationException, SAXException
  {
    Enumeration en = servletConfig.getInitParameterNames();
    boolean foundConfig = false;
    while (en.hasMoreElements())
    {
      String name = (String)en.nextElement();
      String value = servletConfig.getInitParameter(name);

      if (name.startsWith("config"))
      {
        foundConfig = true;

        StringTokenizer st = new StringTokenizer(value, "\n,");
        while (st.hasMoreTokens())
        {
          String fileName = st.nextToken().trim();
          DwrXmlConfigurator local = new DwrXmlConfigurator();
          local.setServletResourceName(fileName);
          local.configure(container);
        }
      }
      else if (name.equals("customConfigurator"))
      {
        foundConfig = true;
        try
        {
          Configurator configurator = (Configurator)LocalUtil.classNewInstance("customConfigurator", value, Configurator.class);
          configurator.configure(container);
          log.debug("Loaded config from: " + value);
        }
        catch (Exception ex)
        {
          log.warn("Failed to start custom configurator", ex);
        }
      }
    }

    return foundConfig;
  }

  public static boolean configureFromAnnotations(Container container)
  {
    try
    {
      Class annotationCfgClass = LocalUtil.classForName("org.directwebremoting.annotations.AnnotationsConfigurator");

      Configurator configurator = (Configurator)annotationCfgClass.newInstance();
      configurator.configure(container);

      log.debug("Java5 AnnotationsConfigurator enabled");
      return true;
    }
    catch (UnsupportedClassVersionError ex)
    {
      return false;
    }
    catch (ClassNotFoundException ex)
    {
      log.warn("AnnotationsConfigurator is missing. Are you running from within an IDE?");
      return false;
    }
    catch (Exception ex)
    {
      log.warn("Failed to start annotations", ex);
      return false;
    }
    catch (Throwable ex)
    {
      if (ex.getClass().getName().equals(UnsupportedClassVersionError.class.getName()))
      {
        log.error("Caught impossible Throwable", ex);
        return false;
      }
      if (ex.getClass().getName().equals(LinkageError.class.getName()))
      {
        log.error("Caught impossible Throwable", ex);
        return false;
      }
      if ((ex instanceof Error))
      {
        log.fatal("Rethrowing Error:" + ex);
        throw ((Error)ex);
      }

      log.error("Ilogical catch state", ex);
    }return false;
  }

  public static void configure(Container container, List configurators)
  {
    for (Iterator it = configurators.iterator(); it.hasNext(); )
    {
      Configurator configurator = (Configurator)it.next();

      log.debug("** Adding config from " + configurator);
      configurator.configure(container);
    }
  }

  public static void configureContainerFully(Container container, ServletConfig servletConfig)
    throws IOException, ParserConfigurationException, SAXException
  {
    configureFromSystemDwrXml(container);
    boolean foundConfig = configureFromInitParams(container, servletConfig);

    boolean skip = Boolean.valueOf(servletConfig.getInitParameter("skipDefaultConfig")).booleanValue();
    IOException delayedIOException = null;
    if ((!foundConfig) && (!skip))
    {
      try
      {
        configureFromDefaultDwrXml(container);
      }
      catch (IOException ex)
      {
        delayedIOException = ex;
      }
    }

    if (!configureFromAnnotations(container))
    {
      log.debug("Java5 AnnotationsConfigurator disabled");

      if (delayedIOException != null)
      {
        throw delayedIOException;
      }
    }
  }

  public static void publishContainer(Container container, ServletConfig servletConfig)
  {
    ServletContext servletContext = servletConfig.getServletContext();

    String publishName = servletConfig.getInitParameter("publishContainerAs");
    if (publishName != null)
    {
      servletContext.setAttribute(publishName, container);
    }

    List containers = (List)servletContext.getAttribute("org.directwebremoting.ContainerList");
    if (containers == null)
    {
      containers = new ArrayList();
    }
    containers.add(container);
    servletContext.setAttribute("org.directwebremoting.ContainerList", containers);
  }

  public static List getAllPublishedContainers(ServletContext servletContext)
  {
    List containers = (List)servletContext.getAttribute("org.directwebremoting.ContainerList");
    if (containers == null)
    {
      containers = new ArrayList();
    }

    return containers;
  }

  public static void shutdownServerLoadMonitorsInContainerList(List containers, String title)
  {
    if ((containers == null) || (containers.size() == 0))
    {
      log.debug("No containers to shutdown for: " + title);
      return;
    }

    log.debug("Shutting down containers for: " + title);
    for (Iterator it = containers.iterator(); it.hasNext(); )
    {
      Container container = (Container)it.next();
      ServerLoadMonitor monitor = (ServerLoadMonitor)container.getBean(ServerLoadMonitor.class.getName());
      monitor.shutdown();
    }
  }

  public static void debugConfig(Container container)
  {
    CreatorManager creatorManager;
    Iterator it;
    if (log.isDebugEnabled())
    {
      log.debug("Container");
      log.debug("  Type: " + container.getClass().getName());
      Collection beanNames = container.getBeanNames();
      for (it = beanNames.iterator(); it.hasNext(); )
      {
        String name = (String)it.next();
        Object object = container.getBean(name);

        if ((object instanceof String))
        {
          log.debug("  Param: " + name + " = " + object + " (" + object.getClass().getName() + ")");
        }
        else
        {
          log.debug("  Bean: " + name + " = " + object + " (" + object.getClass().getName() + ")");
        }

      }

      AccessControl accessControl = (AccessControl)container.getBean(AccessControl.class.getName());
      log.debug("AccessControl");
      log.debug("  Type: " + accessControl.getClass().getName());

      AjaxFilterManager ajaxFilterManager = (AjaxFilterManager)container.getBean(AjaxFilterManager.class.getName());
      log.debug("AjaxFilterManager");
      log.debug("  Type: " + ajaxFilterManager.getClass().getName());

      ConverterManager converterManager = (ConverterManager)container.getBean(ConverterManager.class.getName());
      log.debug("ConverterManager");
      log.debug("  Type: " + converterManager.getClass().getName());

      creatorManager = (CreatorManager)container.getBean(CreatorManager.class.getName());
      log.debug("CreatorManager");
      log.debug("  Type: " + creatorManager.getClass().getName());
      Collection creatorNames = creatorManager.getCreatorNames();
      for (it = creatorNames.iterator(); it.hasNext(); )
      {
        String creatorName = (String)it.next();
        Creator creator = creatorManager.getCreator(creatorName);
        log.debug("  Creator: " + creatorName + " = " + creator + " (" + creator.getClass().getName() + ")");
      }
    }
  }
}